/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package com.chain.code.utils;

import com.azon.guesser.utils.FilledChecker;
import com.azon.ip.utils.ImageWriter;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Mahdan Ahmad F A
 */
public class ThinnedWetLeftHand extends WetLeftHand {

    @Override
    public String[] doWetLeftHand(int[][] matrix) {
        
        ArrayList<String> array = new ArrayList<>();
        
        int[] start = getDeadEnd(matrix, null);
        
        int iteration = 0;
        
        while (((start[0] != -28) && (start[1] != -28)) ){try {
            // && (iteration < 1)) {
            //BufferedImage new_img = ImageWriter.writeImageGrayScale(MatrixConverter.blackAndWhiteReverter(matrix));
            //ImageWriter.WriteImage(new_img, "cobaM//" + iteration + ".jpg");
            
            String chain_code = createAChainCode(matrix, start[0], start[1], iteration);
            if (chain_code.length() < 9) {
                
            } else {
                array.add(chain_code);
                //System.out.println(array.get(iteration));
                //System.out.println("");
                iteration++;
            }
            
            start = getDeadEnd(matrix,null);
            
            //System.out.println(start[0] + " " + start[1]);
            } catch (IOException ex) {
                Logger.getLogger(ThinnedWetLeftHand.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            
            
        }
        
        String[] chain_codes = array.toArray(new String[array.size()]);
        return chain_codes;
    }
    
    private String createAChainCode(int[][] matrix, int startX, int startY, int character) throws IOException {
        String chain_code = "";
        
        ArrayList<int[]> cache = new ArrayList<>();
        cache.add(new int[]{-28,-28});
        int direction   = 0;
        
        Integer[] nextCounter = countNext(matrix, startX, startY);
        
        int posX = startX;
        int posY = startY;
        matrix[posX][posY] = 1;
        
        int[] next = null;
        int   iteration = 0;
        
        boolean queue_flag = false;
        
        while((nextCounter[0] != 0) || !(cache.isEmpty())) {
            //BufferedImage new_img = ImageWriter.writeImageGrayScale(MatrixConverter.blackAndWhiteReverter(matrix));
            //ImageWriter.WriteImage(new_img, "cobaR//" + character + " - " + startX + startY + "-" + iteration + "(" + posX + "," + posY + ")" + "[" + nextCounter[0] + "]" + ".jpg");
            /*
            if ((posX == 7) && (posY == 32)) {
                System.out.println(nextCounter[0]);
            } // */
            if (nextCounter[0] == 0) {
                
            } else if (nextCounter[0] == 1) {
                //System.out.println(" --> " + nextCounter[1]);
                chain_code += nextCounter[1];
                
                next = getNext(posX, posY, nextCounter[1]);
            } else if ((nextCounter[0] == 2)) {
                //System.out.println();
                //chain_code += "*";
                if (chain_code.isEmpty()) {
                    chain_code += nextCounter[1];
                    next = getNext(posX, posY, nextCounter[1]);
                } else {
                    direction   = giveDirection(nextCounter, chain_code);
                    chain_code += "+" + direction;
                    cache.add(next);
                    next = getNext(posX, posY, direction);
                }
            } else if ((nextCounter[0] == 3)) {
                if (chain_code.isEmpty()) {
                    chain_code += nextCounter[1];
                    next = getNext(posX, posY, nextCounter[1]);
                } else {
                    direction   = giveDirection(nextCounter, chain_code);
                    chain_code += "+" + direction;
                    cache.add(next);
                    cache.add(next);
                    next = getNext(posX, posY, direction);
                }
            } else {
                System.out.println("Ada lebih banyak persimpangan " + nextCounter[0]);
                break;
            }

            if (next != null) {
                posX = next[0];
                posY = next[1];

                matrix[posX][posY] = 1;
            }
            if (nextCounter[0] <= 3 ) {
                nextCounter = countNext(matrix, posX, posY);
            } else {
                nextCounter[0] = 0;
                //System.out.println(cache.get(0)[0] + " " + cache.get(0)[1]);
            }
            
            if ((nextCounter[0] == 0) && !cache.isEmpty()) {
                posX = cache.get(cache.size() - 1)[0];
                posY = cache.get(cache.size() - 1)[1];
                
                cache.remove(cache.size() - 1);
                if (((posX == -28) || (posY == -28))) {
                    if ((chain_code.length() < 3)&& ((startX+13 < matrix.length) && (startY-2 > 0) && (startY+2 < matrix[0].length))) {
                        outerloop:
                        for (int m = 0; m < 13; m++) {
                            for (int n = -2; n < 2; n++) {
                                if (matrix[startX+m][startY+n] == 0) {
                                    chain_code = "titik+";
                                    //System.out.println(chain_code);
                                    int[] newStart = getDeadEnd(matrix, new int[]{(startX+m),(startY+n)});
                                    
                                    posX = newStart[0];
                                    posY = newStart[1];
                                    
                                    matrix[posX][posY] = 1;
                                    nextCounter = countNext(matrix, posX, posY);
                                    break outerloop;
                                }
                            }
                        }
                    } // */                    
                } else {
                    matrix[posX][posY] = 1;
                    nextCounter = countNext(matrix, posX, posY);
                    chain_code += "+";
                }
            }
            iteration++;
        }

        return startX + "X" + startY + "Y" + chain_code;
    }
    
    private int[] getDeadEnd(int[][] matrix, int[] accused) {
        
        int[] start       = new int[2];
        int[] actualStart = new int[2];
        
        if (accused == null) {
            actualStart = detectorBlack(matrix);
        } else {
            actualStart = accused;
        }
        
        if ((actualStart[0] != -28) && actualStart[1] != -28) {
            boolean[][] flag_matrix = new boolean[matrix.length][matrix[0].length];

            for (int m = 0; m < matrix.length; m++) {
                for (int n = 0; n < matrix[0].length; n++) {
                    flag_matrix[m][n] = matrix[m][n] == 0;
                }
            }

            Queue<Point> queue = new LinkedList<>();
            queue.add(new Point(actualStart[0], actualStart[1]));

            flag_matrix[actualStart[0]][actualStart[1]] = false;

            while (!queue.isEmpty()) {
                Point pos = queue.remove();

                //System.out.println("NOW!!" + pos.toString());

                if (countNext(matrix, pos.x, pos.y)[0] < 2) {
                    start[0] = pos.x;
                    start[1] = pos.y;
                    break;
                } else {
                    String direction = "12346789";

                    for (int i = 0; i < direction.length(); i++) {
                        if (isAvailable(matrix, pos.x, pos.y, (direction.charAt(i) - 48))) {
                            int next[] = getNext(pos.x, pos.y, (direction.charAt(i) - 48));

                            if (flag_matrix[next[0]][next[1]]) {
                                flag_matrix[next[0]][next[1]] = false;
                                queue.add(new Point(next[0], next[1]));
                            }
                        }
                    }
                }
            }
        }
        if ((start[0] == 0) && start[1] == 0) {
            start[0] = actualStart[0];
            start[1] = actualStart[1];
        }

            //System.out.println(actualStart[0] + " " + actualStart[1]);
            //System.out.println(start[0] + " " + start[1]);

        return start;
    }
    
    private Integer[] countNext(int[][] matrix, int posX, int posY) {
        int next = 0;
           
        ArrayList<Integer> array = new ArrayList<>();
        array.add(0);
        
        String direction = "12346789";
        
        for (int i = 0; i < direction.length(); i++) {
            if (isAvailable(matrix, posX, posY, (direction.charAt(i) - 48))) {
                next++;
                array.set(0, next);
                array.add((direction.charAt(i) - 48));
            }
            
            //System.out.println(isAvailable(matrix, posX, posY, (direction.charAt(i) - 48)));
        }
        
        Integer[] result = array.toArray(new Integer[array.size()]);
        
        return result;
    }

    private boolean isAvailable(int[][] matrix, int startX, int startY, int direction ) {
        
        int[] position = getNext(startX, startY, direction);
        
        int posX = position[0];
        int posY = position[1];
                
        int maxX = matrix.length - 1;
        int maxY = matrix[0].length - 1;
        
        switch(direction) {
            case 1:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startX == 0) {
                    return false;
                } else {
                    if ((matrix[posX][posY] == 0) && (matrix[posX][posY+1] == 1) && (matrix[posX+1][posY] == 1)) {
                        return true;
                    }
                }
                break;
            case 2:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startX == 0) {
                    if (startY == 0) {
                        return false;
                    } else if (matrix[posX][posY] == 0) {
                        return true;
                    }
                } else {
                    if ((matrix[posX][posY] == 0)) {
                        return true;
                    }
                }
                break;
            case 3:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startY == 0) {
                    return false;
                } else {
                    if ((matrix[posX][posY] == 0)&& (matrix[posX][posY-1] == 1) && (matrix[posX+1][posY] == 1)) {
                        return true;
                    }
                }
                break;
            case 4:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startY == maxY) {
                    if (startX == 0) {
                        return false;
                    } else if (matrix[posX][posY] == 1) {
                        return true;
                    }
                } else {
                    if ((matrix[posX][posY] == 0)) {
                        return true;
                    }
                }
                break;
            case 6:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startY == 0) {
                    if (startX == maxX) {
                        return false;
                    } else if (matrix[posX][posY] == 1) {
                        return true;
                    }
                } else {
                    if ((matrix[posX][posY] == 0)) {
                        return true;
                    }
                }
                break;
            case 7:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startX == maxX) {
                    return false;
                } else {
                    if ((matrix[posX][posY] == 0)&& (matrix[posX-1][posY] == 1) && (matrix[posX][posY+1] == 1)) {
                        return true;
                    }
                }
                break;
            case 8:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startX == maxX) {
                    if (startY == maxY) {
                        return false;
                    } else if (matrix[posX][posY] == 1) {
                        return true;
                    }
                } else {
                    if ((matrix[posX][posY] == 0)) {
                        return true;
                    }
                }
                break;
            case 9:
                //System.out.print("Nilai matrik di posisi " + direction + "(" + posX + "," + posY + ")" + " = " + matrix[posX][posY]);
                if (startY == maxY) {
                    return false;
                } else {
                    if ((matrix[posX][posY] == 0)&& (matrix[posX-1][posY] == 1) && (matrix[posX][posY-1] == 1)) {
                        return true;
                    }
                }
                break;
        }
        
        return false;
    }
    
    private boolean isConnected(Point a, Point b) {
        
        double distance = a.distance(b);
        
        return (distance < 1.8);
    }

    private boolean isStraight(int current, int next) {
        return current == next;
    }
    
    private boolean isAngle(int current, int next) {
        switch(current) {
            case 1:
                if ((next == 3) || (next == 7)) {
                    return true;
                }
                
            case 3:
                if ((next == 1) || (next == 9)) {
                    return true;
                }
                
            case 7:
                if ((next == 1) || (next == 9)) {
                    return true;
                }
                
            case 9:
                if ((next == 3) || (next == 7)) {
                    return true;
                }
                
            default:
                return false;      
        }
    }
    
    private int giveDirection(Integer[] counter, String code) {
        int direction   = 0;
        int current     = 0;
        int iteration   = 1;
        
        char cache;
        
        if (code.isEmpty()) {
            return counter[1];
        } else {
            cache = code.charAt(code.length() - iteration);
            while (cache == '+') {
                iteration++;
                cache = code.charAt(code.length() - iteration);
            }
            current = Integer.parseInt("" + cache);
        }
        /*
        //System.out.print("Current = " + current + " ");
        
        for (int i = 1; i < counter.length; i++) {
            System.out.print(counter[i] + " ");
        } // */
        
        for (int i = 1; i < counter.length; i++) {
            //System.out.println(isStraight(current, counter[i]));
            if (isStraight(current, counter[i])) {
                return counter[i];
            }
        }
        
        for (int i = 1; i < counter.length; i++) {
            if (isAngle(current, counter[i])) {
                return counter[i];
            }
        }
        
        if (current == 8) {
            String sequence = FilledChecker.getSequence(code).replaceAll("\\+", "");
            int    previous = 0;
            if (code.length() > 2) {
                previous = Integer.parseInt("" + sequence.charAt((sequence.length() - 2)));
            }
  //          System.out.println(previous);
            if ((current - previous) > 0) {
                return pick47(counter);
            } else {
                return pick69(counter);
            }
        } // */
        
        direction = counter[1];
        
        return direction;
    }
    
    private int pick47(Integer[] some) {
        for (int i = 1; i < some.length; i++) {
            if (some[i] == 7) {
                return 7;
            } else if (some[i] == 4) {
                return 4;
            }
        }
        
        return 8;
    }
  
    private int pick69(Integer[] some) {
        for (int i = 1; i < some.length; i++) {
            if (some[i] == 9) {
                return 9;
            } else if (some[i] == 6) {
                return 6;
            }
        }
        
        return 8;
    }
    
}
